package svgedit.xml;

import svgedit.svg.*;
import java.io.*;

import org.w3c.dom.*;
import javax.xml.parsers.*;

/**
 * Class to write SVG document out to xml.
 * 
 * @author SARJ
 * 
 */
public class SVGFileWriter
{
	private Document xml;
	private Element root;
	private SVGDocument svg;

	public SVGFileWriter()
	{
	}

	/**
	 * 
	 * @param fn
	 *            - File to write to.
	 * @param doc
	 *            - the SVG structure
	 * @throws IOException
	 *             - error's while writing
	 * @throws Exception
	 *             - un-calculated exceptions
	 * 
	 *             writes out the xml document to file
	 * 
	 */
	public void write(File fn, SVGDocument doc) throws IOException, Exception
	{
		System.out.println("Writing to file");

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = dbf.newDocumentBuilder();

		this.svg = doc;
		this.xml = builder.newDocument();

		// create the svg element.
		this.root = createSVGElm((this.xml), doc);
		(this.xml).appendChild(root);

		// write nodes.
		writeDoc();
		XMLUtil.write(this.xml, fn);

	}

	/**
	 * WriteDoc - iterate over the <svg> element and write groups or elements
	 * depending on the structure
	 */
	private void writeDoc()
	{
		for (SVGElement element : (this.svg).getRootGroup())
		{
			if (element instanceof SVGGroup)
			{
				System.out.println(element);
				writeGroup(this.root, (SVGGroup) element);
			}
			else
				writeElement(this.root, element);

		}
	}

	/**
	 * 
	 * @param groupDomParent
	 *            - the parent node to add the new group to.
	 * @param group
	 *            - the new group to be added.
	 * 
	 *            writeGroup nests group's and add's elements to the newly
	 *            created group
	 */
	private void writeGroup(Element groupDomParent, SVGGroup group)
	{
		Element groupDomElement = createGroup(group);

		writeElement(groupDomElement, group);
		groupDomParent.appendChild(groupDomElement);

		for (SVGElement element : group)
		{
			System.out.println("Chilrdren :" + element.toString());

			if (element instanceof SVGGroup)
			{
				writeGroup(groupDomElement, (SVGGroup) element);
			}
			else
			{
				writeElement(groupDomElement, element);
			}
		}

	}

	/**
	 * 
	 * @param groupDomElement
	 *            - Node to add the element to.
	 * @param element
	 *            - the element to be added
	 * 
	 *            WriteElement is a dispatch to write each of the different
	 *            element types: Rect, line and circle and calls the appropriate
	 *            write method to add to the xml structure.
	 */
	private void writeElement(Element groupDomElement, SVGElement element)
	{

		if (element instanceof SVGCircleElement)
			groupDomElement
					.appendChild(writeCircle((SVGCircleElement) element));

		else if (element instanceof SVGRectElement)
			groupDomElement.appendChild(writeRect((SVGRectElement) element));

		else if (element instanceof SVGLineElement)
			groupDomElement.appendChild(writeLine((SVGLineElement) element));

	}

	/**
	 * 
	 * @param SVGCircleElement
	 *            circle
	 * @return The constructed xml element from circle. constructs a "<circle>"
	 *         element.
	 */
	private Node writeCircle(SVGCircleElement circle)
	{

		Element temp = (this.xml).createElement("circle");
		temp.setAttribute("cx", circle.getCX().valueAsString());
		temp.setAttribute("cy", circle.getCY().valueAsString());
		temp.setAttribute("r", circle.getR().valueAsString());
		temp.setAttribute("fill", circle.getFill().valueAsString());
		temp.setAttribute("stroke", circle.getStroke().valueAsString());
		temp.setAttribute("stroke-width", circle.getStrokeWidth()
				.valueAsString());

		return temp;

	}

	/**
	 * 
	 * @param SVGRectElement
	 *            rect
	 * @return The constructed xml element from rect Constructs a "<rect>"
	 *         element.
	 */
	private Node writeRect(SVGRectElement rect)
	{
		Element temp = (this.xml).createElement("rect");
		// x, y, width, height, fill, stroke
		temp.setAttribute("x", rect.getX().valueAsString());
		temp.setAttribute("y", rect.getY().valueAsString());
		temp.setAttribute("width", rect.getWidth().valueAsString());
		temp.setAttribute("height", rect.getHeight().valueAsString());
		temp.setAttribute("fill", rect.getFill().valueAsString());
		temp.setAttribute("stroke", rect.getStroke().valueAsString());
		temp.setAttribute("stroke-width", rect.getStrokeWidth().valueAsString());

		return temp;
	}

	/**
	 * 
	 * @param SVGLineElement
	 *            line
	 * @return the constructed xml element from line. Constructs a "<line>"
	 *         element.
	 */
	private Node writeLine(SVGLineElement line)
	{
		Element temp = (this.xml).createElement("line");
		// x, y, x2, ye, fill, stroke
		temp.setAttribute("fill", line.getFill().valueAsString());
		temp.setAttribute("stroke", line.getStroke().valueAsString());
		temp.setAttribute("stroke-width", line.getStrokeWidth().valueAsString());
		temp.setAttribute("x1", line.getX1().valueAsString());
		temp.setAttribute("y1", line.getY1().valueAsString());
		temp.setAttribute("x2", line.getX2().valueAsString());
		temp.setAttribute("y2", line.getY2().valueAsString());

		return temp;
	}

	/**
	 * 
	 * @param d
	 *            - the DOM xml Document
	 * @param svg
	 *            - the SVGDocument
	 * @return the "<svg>" element
	 */
	private Element createSVGElm(Document d, SVGDocument svg)
	{

		Element temp = d.createElement("svg");

		temp.setAttribute("xmlns", "http://www.w3.org/2000/svg");

		temp.setAttribute("height", (svg.getHeight().valueAsString()));
		temp.setAttribute("width", (svg.getWidth().valueAsString()));
		temp.setAttribute("viewBox", "0 0" + (svg.getViewportWidth()) + " "
				+ (svg.getViewportHeight()));

		return temp;
	}

	private Element createGroup(SVGGroup g)
	{
		return (this.xml).createElement("g");
	}

}
